package com.usbserial.scale.adapter.aclas;

import com.usbserial.core.SerialConfig;
import com.usbserial.core.SerialPort;
import com.usbserial.core.UsbSerialDeviceInfo;
import com.usbserial.exception.ParseException;
import com.usbserial.exception.UsbSerialException;
import com.usbserial.scale.ScaleData;
import com.usbserial.scale.adapter.ScaleAdapter;
import com.usbserial.scale.adapter.ScaleCommand;

import java.nio.charset.StandardCharsets;

/**
 * Aclas 顶尖电子秤适配器抽象基类
 *
 * 支持的设备型号：
 * - Aclas OS2X (HX15XYS系列, 最大15kg)
 * - Aclas OSX-15 (最大15kg)
 *
 * 通信协议：
 * - 协议类型：被动推送模式（设备主动发送数据）
 * - USB芯片：CH340 (VID=0x1A86 PID=0x7523)
 * - 串口配置：9600/8N1
 * - 帧格式：16字节固定长度
 *
 * 数据帧结构：
 * - [SOH(0x01)][STX(0x02)][数据11字节][ETX(0x03)][EOT(0x04)][校验1字节]
 * - 数据部分：[状态1字节][空格][重量5-7字节][单位2-3字节][校验字符1字节]
 * - 示例：01 02 53 20 30 30 2E 37 30 36 6B 67 60 03 04 00
 *   解码：SOH STX S 00.706kg` ETX EOT NULL
 *
 * 协议要点：
 * 1. 状态字节（S/U/O/Z/T）是稳定性判断的权威来源
 *    - S = Stable (稳定)
 *    - U = Unstable (不稳定)
 *    - O = Overload (超载)
 *    - Z = Zero (零点)
 *    - T = Tare (去皮)
 *
 * 2. 最后一个字符是校验位，会随重量数据变化：
 *    - 0.706kg → 0x60 ('`')
 *    - 0.894kg → 0x64 ('d')
 *    - 0.898kg → 0x6E ('n')
 *    - 不是固定的"稳定标志"，解析时应忽略
 *
 * 3. 单位支持：kg (千克), g (克), lb (磅), oz (盎司)
 *
 * 子类只需实现：
 * - getAdapterName() - 适配器名称
 * - getAdapterVersion() - 适配器版本
 * - getModelName() - 设备型号
 * - getDeviceIdentifier() - 设备识别信息
 */
public abstract class AbstractAclasAdapter implements ScaleAdapter {

    // 协议常量
    protected static final int FRAME_LENGTH = 16;
    protected static final byte SOH = 0x01;  // Start of Header
    protected static final byte STX = 0x02;  // Start of Text
    protected static final byte ETX = 0x03;  // End of Text
    protected static final byte EOT = 0x04;  // End of Transmission

    // 数据偏移位置
    protected static final int OFFSET_SOH = 0;
    protected static final int OFFSET_STX = 1;
    protected static final int OFFSET_DATA_START = 2;
    protected static final int OFFSET_DATA_END = 13;
    protected static final int OFFSET_ETX = 13;
    protected static final int OFFSET_EOT = 14;

    protected SerialPort port;
    protected UsbSerialDeviceInfo deviceInfo;

    /**
     * 获取适配器名称（子类实现）
     */
    protected abstract String getAdapterName();

    /**
     * 获取适配器版本（子类实现）
     */
    protected abstract String getAdapterVersion();

    /**
     * 获取设备型号（子类实现）
     */
    protected abstract String getModelName();

    @Override
    public String getName() {
        return getAdapterName();
    }

    @Override
    public String getVersion() {
        return getAdapterVersion();
    }

    @Override
    public void initialize(SerialPort port, UsbSerialDeviceInfo deviceInfo) throws UsbSerialException {
        this.port = port;
        this.deviceInfo = deviceInfo;
        // Aclas 系列是被动设备，自动发送数据，无需额外初始化
    }

    @Override
    public ScaleData parseData(byte[] rawData) throws ParseException {
        // 验证数据长度
        if (rawData == null || rawData.length != FRAME_LENGTH) {
            throw new ParseException("Invalid frame length: expected " + FRAME_LENGTH +
                                   ", got " + (rawData == null ? "null" : rawData.length));
        }

        // 验证帧头和帧尾
        if (rawData[OFFSET_SOH] != SOH) {
            throw new ParseException("Invalid SOH: expected 0x01, got 0x" +
                                   Integer.toHexString(rawData[OFFSET_SOH] & 0xFF));
        }
        if (rawData[OFFSET_STX] != STX) {
            throw new ParseException("Invalid STX: expected 0x02, got 0x" +
                                   Integer.toHexString(rawData[OFFSET_STX] & 0xFF));
        }
        if (rawData[OFFSET_ETX] != ETX) {
            throw new ParseException("Invalid ETX: expected 0x03, got 0x" +
                                   Integer.toHexString(rawData[OFFSET_ETX] & 0xFF));
        }
        if (rawData[OFFSET_EOT] != EOT) {
            throw new ParseException("Invalid EOT: expected 0x04, got 0x" +
                                   Integer.toHexString(rawData[OFFSET_EOT] & 0xFF));
        }

        // 提取数据部分
        int dataLength = OFFSET_DATA_END - OFFSET_DATA_START;
        byte[] dataBytes = new byte[dataLength];
        System.arraycopy(rawData, OFFSET_DATA_START, dataBytes, 0, dataLength);

        // 转换为ASCII字符串
        String dataStr = new String(dataBytes, StandardCharsets.US_ASCII).trim();

        // 解析数据
        return parseAclasFormat(dataStr);
    }

    /**
     * 解析Aclas格式的数据字符串
     * 格式：[状态][空格][重量][单位][校验字符]
     * 示例：S 00.706kg` (OS2X) 或 S 00.698kgf (OSX-15)
     *
     * 注意：
     * - 状态字符(S/U/O/Z/T)是稳定性的权威来源
     * - 最后一个字符是校验位或数据相关字段，会随重量变化，应该被忽略
     */
    protected ScaleData parseAclasFormat(String data) throws ParseException {
        if (data.length() < 3) {
            throw new ParseException("Data too short: " + data);
        }

        // 解析状态字符（第一个字符）- 这是稳定性的权威来源
        char statusChar = data.charAt(0);
        ScaleData.ScaleStatus status = parseStatus(statusChar);

        // 跳过状态和空格，提取剩余部分
        String remaining = data.substring(1).trim();

        // 查找单位的开始位置（第一个非数字非点号非符号字符）
        int unitStart = findUnitStart(remaining);
        if (unitStart == -1) {
            throw new ParseException("Cannot find unit in data: " + data);
        }

        // 提取重量值部分
        String weightStr = remaining.substring(0, unitStart).trim();
        double weight = parseWeight(weightStr);

        // 提取单位部分（包含最后的校验字符）
        String unitPart = remaining.substring(unitStart);

        // 去除最后一个字符（校验位/数据相关字符）
        // 该字符会随重量值变化，不是固定的稳定标志
        String unitStr;
        if (unitPart.length() > 1) {
            unitStr = unitPart.substring(0, unitPart.length() - 1);
        } else {
            unitStr = unitPart;
        }

        ScaleData.WeightUnit unit = parseUnit(unitStr);

        // 直接使用状态字符判断的结果，不依赖最后的校验字符
        return new ScaleData(weight, unit, status);
    }

    /**
     * 查找单位的开始位置
     */
    protected int findUnitStart(String str) {
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (!Character.isDigit(c) && c != '.' && c != '-' && c != '+' && c != ' ') {
                return i;
            }
        }
        return -1;
    }

    /**
     * 解析状态字符
     */
    protected ScaleData.ScaleStatus parseStatus(char statusChar) {
        switch (statusChar) {
            case 'S':  // Stable
                return ScaleData.ScaleStatus.STABLE;
            case 'U':  // Unstable
                return ScaleData.ScaleStatus.UNSTABLE;
            case 'O':  // Overload
                return ScaleData.ScaleStatus.OVERLOAD;
            case 'Z':  // Zero
                return ScaleData.ScaleStatus.ZERO;
            case 'T':  // Tare
                return ScaleData.ScaleStatus.TARE;
            default:
                return ScaleData.ScaleStatus.UNSTABLE;
        }
    }

    /**
     * 解析重量值
     */
    protected double parseWeight(String weightStr) throws ParseException {
        try {
            return Double.parseDouble(weightStr);
        } catch (NumberFormatException e) {
            throw new ParseException("Invalid weight value: " + weightStr, e);
        }
    }

    /**
     * 解析单位
     */
    protected ScaleData.WeightUnit parseUnit(String unitStr) {
        String normalized = unitStr.trim().toLowerCase();
        switch (normalized) {
            case "g":
            case "gram":
                return ScaleData.WeightUnit.GRAM;
            case "kg":
            case "kilogram":
                return ScaleData.WeightUnit.KILOGRAM;
            case "lb":
            case "lbs":
            case "pound":
                return ScaleData.WeightUnit.POUND;
            case "oz":
            case "ounce":
                return ScaleData.WeightUnit.OUNCE;
            default:
                // 默认返回克
                return ScaleData.WeightUnit.GRAM;
        }
    }

    @Override
    public void sendCommand(ScaleCommand command, Object... params) throws UsbSerialException {
        // Aclas 系列是被动设备，不支持发送命令
        throw new UnsupportedOperationException(
            "Aclas devices are passive and do not support commands");
    }

    @Override
    public SerialConfig getRecommendedConfig() {
        return SerialConfig.builder()
            .baudRate(9600)
            .dataBits(SerialConfig.DATABITS_8)
            .stopBits(SerialConfig.STOPBITS_1)
            .parity(SerialConfig.PARITY_NONE)
            .flowControl(SerialConfig.FLOW_CONTROL_OFF)
            .build();
    }

    @Override
    public void release() {
        // 释放资源
        this.port = null;
        this.deviceInfo = null;
    }
}
